/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef OLA_COAP_NET_H
#define OLA_COAP_NET_H
#include "ola_sdk_api.h"
#include "ola_pal_socket.h"
#include "ola_coap_def.h"
#include "ola_pal_os.h"

#ifdef __cplusplus
extern "C" {
#endif

struct OlaCoapResendItem;
struct OlaCoapReqItem;
struct OlaCoapSession;
struct OlaCoapResource;
struct OlaCoapRecvItem;
struct OlaCoapContext;
typedef struct OlaCoapResendItem OlaCoapResendItem;
typedef struct OlaCoapReqItem OlaCoapReqItem;
typedef struct OlaCoapSession OlaCoapSession;
typedef struct OlaCoapResource OlaCoapResource;
typedef struct OlaCoapRecvItem OlaCoapRecvItem;
typedef struct OlaCoapContext OlaCoapContext;

typedef enum {
    /* 交互第1条报文间隔100ms发送，直到对方响应 */
    OLA_COAP_RESEND_TYPE_FIRST_MSG = 0,
    /* 交互最后1条报文，间隔100ms发送5次 */
    OLA_COAP_RESEND_TYPE_LAST_MSG,
    /* 中间报文间隔1s发送5次 */
    OLA_COAP_RESEND_TYPE_OTHER,
} OlaCoapResendType;

typedef enum {
    OLA_COAP_PROTOCOL_TYPE_UDP = 0,
    OLA_COAP_PROTOCOL_TYPE_TLS,
} OlaCoapProtocolType;

typedef enum {
    OLA_COAP_CONTEXT_TYPE_CLIENT = 0,
    OLA_COAP_CONTEXT_TYPE_SERVER,
} OlaCoapContextType;

typedef enum {
    OLA_COAP_PACKET_TYPE_REQUEST = 0,
    OLA_COAP_PACKET_TYPE_RESPONSE,
    OLA_COAP_PACKET_TYPE_EMPTY,
} OlaCoapPacketType;

struct OlaCoapSession {
    OlaSockaddr addr;
    bool overFlag;
    OlaCoapContext *context;
    unsigned short sendMsgId;
    unsigned short recvMsgId;
    uint64_t token;
    unsigned long long lastSendTime;
    unsigned long long lastRecvTime;
    OlaCoapSession *next;
};

typedef enum {
    OLA_COAP_EVENT_SOCKET_SELECT_ERROR = 0,
    OLA_COAP_EVENT_SOCKET_READ_ERROR,
    OLA_COAP_EVENT_SOCKET_WRITE_ERROR,
} OlaCoapEvent;

/*
 * 功能: request处理函数
 * 参数: session，request所属会话
 *       pkt，收到的coap报文
 *       outData，回复的内容，由处理函数内部分配内存
 *       id，请求id，如果需要异步回复
 * 返回: OLA_OK，处理成功
 *       OLA_COAP_RESP_LAST_MSG，处理成功且为交互最后一条报文，用于udp重发
 *       OLA_COAP_RESP_NOT_RESP，不回复报文
 *       其他，处理失败
 */
typedef int (*OlaCoapRequestHandler)(OlaCoapSession *session, const OlaCoapPacket *pkt,
    OlaData *outData, OlaReqId reqId);

struct OlaCoapResource {
    unsigned char uri[OLA_COAP_URI_MAX_LEN];
    OlaCoapMethodType method;
    OlaCoapRequestHandler handler;
    OlaCoapResource *next;
};

typedef void (*OlaCoapResponseHandler)(OlaCoapSession *session, const OlaCoapPacket *pkt);

typedef void (*OlaCoapEventHandler)(OlaCoapContext *context, OlaCoapEvent event);

struct OlaCoapReqItem {
    unsigned short msgId;
    unsigned char tokenLen;
    unsigned char token[OLA_COAP_TOKEN_MAX_LEN];
    unsigned long long reqTime;
    OlaCoapResponseHandler handler;
    OlaCoapSession *session;
    OlaCoapReqItem *next;
};

struct OlaCoapResendItem {
    OlaCoapResendType resendFlag;
    unsigned char sendCount;
    unsigned long long lastSendTime;
    OlaCoapPacket packet;
    OlaBuffer buf;
    OlaCoapSession *session;
    OlaCoapResendItem *next;
};

struct OlaCoapRecvItem {
    unsigned short msgId;
    unsigned char tokenLen;
    unsigned char token[OLA_COAP_TOKEN_MAX_LEN];
    unsigned long long recvTime;
    OlaCoapSession *session;
    OlaCoapRecvItem *next;
};

struct OlaCoapContext {
    bool useFlag;
    bool errFlag;
    OlaCoapProtocolType protocol;
    OlaCoapContextType type;
    int fd;
    OlaMutex mutex;
    OlaCoapSession *session;
    OlaCoapResource *resource;
    OlaCoapResendItem *resendMsg;
    OlaCoapReqItem *reqMsg;
    OlaCoapRecvItem *recvMsg;
    OlaBuffer recvBuf;
    OlaBuffer sendBuf;
    OlaCoapEventHandler eventHandler;
    void *ssl;
    unsigned long long dumpTime;
};

typedef struct {
    OlaCoapPacketType type;
    OlaReqId reqId;
    OlaCoapMethodType method;
    unsigned char optionsNum;
    OlaCoapOption options[OLA_COAP_OPTION_MAX_NUM];
    OlaCoapResponseHandler handler;
} OlaCoapParam;

/*
 * 功能: 创建新的coap上下文
 * 参数: url，目标url，为NULL时会作为服务端监听本地端口
 *       port，目标端口或者本地监听端口
 *       type，会话类型
 * 返回: NULL，创建会话失败
 *       非NULL，会话句柄
 */
OlaCoapContext *OlaCoapNewContext(const char *url, unsigned short port, OlaCoapProtocolType type);

/*
 * 功能: 释放coap上下文
 * 参数: ctx，待释放的coap上下文
 * 返回: OLA_OK，释放成功
 *       非OLA_OK，释放失败
 */
int OlaCoapFreeContext(OlaCoapContext *ctx);

/*
 * 功能: 使用指定会话发送COAP数据
 * 参数: session，使用会话句柄
 *       data，待发送发送数据
 *       param，COAP报文参数
 *       id，当发送的数据是异步处理结果时需附带id
 * 返回: OLA_OK，处理成功
 *       非OLA_OK，处理失败
 * 注意: 发送为同步处理，收到的响应通过param中的handler通知
 */
int OlaCoapSendData(OlaCoapSession *session, const OlaData *data, const OlaCoapParam *param);

/*
 * 功能: 为指定上下文添加服务资源，添加后COAP报文会根据URI分发至相应的处理函数
 * 参数: session，指定的会话句柄
 *       resource，资源数组
 *       resNum，资源数组大小
 * 返回: OLA_OK，添加成功
 *       非OLA_OK，添加失败
 */
int OlaCoapRegisterResource(OlaCoapContext *ctx, const OlaCoapResource *resource);

/*
 * 功能: 为指定会话添加事件处理函数
 * 参数: session，指定的会话句柄
 *       handler，待注册的处理函数
 * 返回: OLA_OK，添加成功
 *       非OLA_OK，添加失败
 */
int OlaCoapRegisterEventHandler(OlaCoapContext *ctx, OlaCoapEventHandler handler);

/*
 * 功能: 执行一次COAP会话的报文接收以及分发
 * 参数: timeout，通过select等待报文的超时时间，建议涉及udp会话为100ms，其他1s
 * 返回: OLA_OK，执行成功
 *       非OLA_OK，执行失败
 */
int OlaCoapProcess(unsigned int timeout);

/*
 * 功能: 查询当前上下文是否有未完成的请求或重发
 * 参数: ctx，指定coap上下文
 * 返回: true，无未完成的交互
 *       false，有未完成的交互
 */
bool OlaIsCoapCanExit(OlaCoapContext *ctx);

int OlaCoapInit(unsigned int recvBufSize, unsigned int sendBufSize);

int OlaCoapDeinit(void);

#ifdef __cplusplus
}
#endif

#endif /* OLA_COAP_NET_H */